<!DOCTYPE HTML><html lang="zh-CN"><head><meta charset="utf-8"><meta name="keywords" content="JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】, 丁一的博客"><meta name="description" content="内存动态分配和垃圾收集技术是JAVA和C++之间最大的区别之一
垃圾收集（Garbage Collection，GC）只办三件事：


哪些内存需要回收


什么时候回收


如何回收


对于对象回收的方法


引用计数法：


每处引用"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"><meta name="renderer" content="webkit|ie-stand|ie-comp"><meta name="mobile-web-app-capable" content="yes"><meta name="format-detection" content="telephone=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="referrer" content="no-referrer-when-downgrade"><title>JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】 | 丁一的博客</title><link rel="icon" type="image/png" href="/medias/comment_bg.png"><link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css"><link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css"><link rel="stylesheet" type="text/css" href="/libs/aos/aos.css"><link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css"><link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css"><link rel="stylesheet" type="text/css" href="/css/matery.css"><link rel="stylesheet" type="text/css" href="/css/my.css"><script src="/libs/jquery/jquery-3.6.0.min.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css"><style type="text/css" lang="css">#loading-container{position:fixed;top:0;left:0;min-height:100vh;width:100vw;z-index:9999;display:flex;flex-direction:column;justify-content:center;align-items:center;background:#fff;text-align:center;-webkit-transition:opacity 1s ease;-moz-transition:opacity 1s ease;-o-transition:opacity 1s ease;transition:opacity 1s ease}.loading-image{width:120px;height:50px;transform:translate(-50%)}.loading-image div:nth-child(2){-webkit-animation:pacman-balls 1s linear 0s infinite;animation:pacman-balls 1s linear 0s infinite}.loading-image div:nth-child(3){-webkit-animation:pacman-balls 1s linear .33s infinite;animation:pacman-balls 1s linear .33s infinite}.loading-image div:nth-child(4){-webkit-animation:pacman-balls 1s linear .66s infinite;animation:pacman-balls 1s linear .66s infinite}.loading-image div:nth-child(5){-webkit-animation:pacman-balls 1s linear .99s infinite;animation:pacman-balls 1s linear .99s infinite}.loading-image div:first-of-type{width:0;height:0;border:25px solid #49b1f5;border-right-color:transparent;border-radius:25px;-webkit-animation:rotate_pacman_half_up .5s 0s infinite;animation:rotate_pacman_half_up .5s 0s infinite}.loading-image div:nth-child(2){width:0;height:0;border:25px solid #49b1f5;border-right-color:transparent;border-radius:25px;-webkit-animation:rotate_pacman_half_down .5s 0s infinite;animation:rotate_pacman_half_down .5s 0s infinite;margin-top:-50px}@-webkit-keyframes rotate_pacman_half_up{0%{transform:rotate(270deg)}50%{transform:rotate(1turn)}to{transform:rotate(270deg)}}@keyframes rotate_pacman_half_up{0%{transform:rotate(270deg)}50%{transform:rotate(1turn)}to{transform:rotate(270deg)}}@-webkit-keyframes rotate_pacman_half_down{0%{transform:rotate(90deg)}50%{transform:rotate(0)}to{transform:rotate(90deg)}}@keyframes rotate_pacman_half_down{0%{transform:rotate(90deg)}50%{transform:rotate(0)}to{transform:rotate(90deg)}}@-webkit-keyframes pacman-balls{75%{opacity:.7}to{transform:translate(-100px,-6.25px)}}@keyframes pacman-balls{75%{opacity:.7}to{transform:translate(-100px,-6.25px)}}.loading-image div:nth-child(3),.loading-image div:nth-child(4),.loading-image div:nth-child(5),.loading-image div:nth-child(6){background-color:#49b1f5;width:15px;height:15px;border-radius:100%;margin:2px;width:10px;height:10px;position:absolute;transform:translateY(-6.25px);top:25px;left:100px}.loading-text{margin-bottom:20vh;text-align:center;color:#2c3e50;font-size:2rem;box-sizing:border-box;padding:0 10px;text-shadow:0 2px 10px rgba(0,0,0,.2)}@media only screen and (max-width:500px){.loading-text{font-size:1.5rem}}.fadeout{opacity:0}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0)}}</style><script>(function () {
    const loaded = function(){
       setTimeout(function(){
            const loader = document.getElementById("loading-container");
            loader.className="fadeout" ;//使用渐隐的方法淡出loading page
            // document.getElementById("body-wrap").style.display="flex";
            setTimeout(function(){
                loader.style.display="none";
            },1000); 
        },1000);//强制显示loading page 1s  
    };
    loaded();
})()</script><meta name="generator" content="Hexo 5.4.2"><style>.github-emoji{position:relative;display:inline-block;width:1.2em;min-height:1.2em;overflow:hidden;vertical-align:top;color:transparent}.github-emoji>span{position:relative;z-index:10}.github-emoji .fancybox,.github-emoji img{margin:0!important;padding:0!important;border:none!important;outline:0!important;text-decoration:none!important;user-select:none!important;cursor:auto!important}.github-emoji img{height:1.2em!important;width:1.2em!important;position:absolute!important;left:50%!important;top:50%!important;transform:translate(-50%,-50%)!important;user-select:none!important;cursor:auto!important}.github-emoji-fallback{color:inherit}.github-emoji-fallback img{opacity:0!important}</style><link rel="alternate" href="/atom.xml" title="丁一的博客" type="application/atom+xml"><link rel="stylesheet" href="/css/prism.css" type="text/css"></head><script src="https://cdn.jsdelivr.net/npm/bluebird@3/js/browser/bluebird.min.js"></script><script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@2.0.3/fetch.min.js"></script><script>fetch("https://v1.hitokoto.cn").then(function(t){return t.json()}).then(function(t){document.getElementById("hitokoto").innerText=t.hitokoto+"——【"+t.from+"】"}).catch(function(t){console.error(t)})</script><div id="loading-container"><p class="loading-text">玩命加载中 . . .</p><div class="loading-image"><div></div><div></div><div></div><div></div><div></div></div></div><body><div class="stars-con"><div id="stars"></div><div id="stars2"></div><div id="stars3"></div></div><svg aria-hidden="true" style="position:absolute;overflow:hidden;width:0;height:0"><symbol id="icon-sun" viewBox="0 0 1024 1024"><path d="M960 512l-128 128v192h-192l-128 128-128-128H192v-192l-128-128 128-128V192h192l128-128 128 128h192v192z" fill="#FFD878" p-id="8420"></path><path d="M736 512a224 224 0 1 0-448 0 224 224 0 1 0 448 0z" fill="#FFE4A9" p-id="8421"></path><path d="M512 109.248L626.752 224H800v173.248L914.752 512 800 626.752V800h-173.248L512 914.752 397.248 800H224v-173.248L109.248 512 224 397.248V224h173.248L512 109.248M512 64l-128 128H192v192l-128 128 128 128v192h192l128 128 128-128h192v-192l128-128-128-128V192h-192l-128-128z" fill="#4D5152" p-id="8422"></path><path d="M512 320c105.888 0 192 86.112 192 192s-86.112 192-192 192-192-86.112-192-192 86.112-192 192-192m0-32a224 224 0 1 0 0 448 224 224 0 0 0 0-448z" fill="#4D5152" p-id="8423"></path></symbol><symbol id="icon-moon" viewBox="0 0 1024 1024"><path d="M611.370667 167.082667a445.013333 445.013333 0 0 1-38.4 161.834666 477.824 477.824 0 0 1-244.736 244.394667 445.141333 445.141333 0 0 1-161.109334 38.058667 85.077333 85.077333 0 0 0-65.066666 135.722666A462.08 462.08 0 1 0 747.093333 102.058667a85.077333 85.077333 0 0 0-135.722666 65.024z" fill="#FFB531" p-id="11345"></path><path d="M329.728 274.133333l35.157333-35.157333a21.333333 21.333333 0 1 0-30.165333-30.165333l-35.157333 35.157333-35.114667-35.157333a21.333333 21.333333 0 0 0-30.165333 30.165333l35.114666 35.157333-35.114666 35.157334a21.333333 21.333333 0 1 0 30.165333 30.165333l35.114667-35.157333 35.157333 35.157333a21.333333 21.333333 0 1 0 30.165333-30.165333z" fill="#030835" p-id="11346"></path></symbol></svg> <a onclick="switchNightMode()" class="icon-V hidden" title="切换主题"><svg width="48" height="48" viewBox="0 0 1024 1024"><use id="modeicon" xlink:href="#icon-moon"></use></svg></a><script>function checkNightMode(){"1"===localStorage.getItem("isDark")||"0"!==localStorage.getItem("isDark")&&(20<=(new Date).getHours()||(new Date).getHours()<7||matchMedia("(prefers-color-scheme: dark)").matches)?($("body").addClass("DarkMode"),$("#changeMode-top").removeClass("fa-moon").addClass("fa-sun"),$("#modeicon").attr("xlink:href","#icon-sun")):$("#modeicon").attr("xlink:href","#icon-moon")}function switchNightMode(){$('<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"></div></div>').appendTo($("body")),setTimeout(function(){$("body").hasClass("DarkMode")?($("body").removeClass("DarkMode"),localStorage.setItem("isDark","0"),$("#changeMode-top").removeClass("fa-sun").addClass("fa-moon"),$("#modeicon").attr("xlink:href","#icon-moon")):($("body").addClass("DarkMode"),localStorage.setItem("isDark","1"),$("#changeMode-top").removeClass("fa-moon").addClass("fa-sun"),$("#modeicon").attr("xlink:href","#icon-sun")),setTimeout(function(){$(".Cuteen_DarkSky").fadeOut(1e3,function(){$(this).remove()})},2e3)})}function switchNightModeTop(){$("body").hasClass("DarkMode")?($("body").removeClass("DarkMode"),localStorage.setItem("isDark","0"),$("#changeMode-top").removeClass("fa-sun").addClass("fa-moon"),$("#modeicon").attr("xlink:href","#icon-moon")):($("body").addClass("DarkMode"),localStorage.setItem("isDark","1"),$("#changeMode-top").removeClass("fa-moon").addClass("fa-sun"),$("#modeicon").attr("xlink:href","#icon-sun"))}</script><header class="navbar-fixed"><nav id="headNav" class="bg-color nav-transparent"><div id="navContainer" class="nav-wrapper container"><div class="brand-logo"><a href="/" class="waves-effect waves-light"><div><img src="/medias/book.gif" class="logo-img" alt="LOGO"> <span class="logo-span">丁一的博客</span></div></a></div><a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a><ul class="right nav-menu"><li class="hide-on-med-and-down nav-item"><a href="/" class="waves-effect waves-light"><i class="fas fa-home" style="zoom:.6"></i> <span>首页</span></a></li><li class="hide-on-med-and-down nav-item"><a href="/tags" class="waves-effect waves-light"><i class="fas fa-tags" style="zoom:.6"></i> <span>标签</span></a></li><li class="hide-on-med-and-down nav-item"><a href="/categories" class="waves-effect waves-light"><i class="fas fa-bookmark" style="zoom:.6"></i> <span>分类</span></a></li><li class="hide-on-med-and-down nav-item"><a href="/archives" class="waves-effect waves-light"><i class="fas fa-archive" style="zoom:.6"></i> <span>时间轴</span></a></li><li class="hide-on-med-and-down nav-item"><a href="/about" class="waves-effect waves-light"><i class="fas fa-user-circle" style="zoom:.6"></i> <span>关于</span></a></li><li class="hide-on-med-and-down nav-item"><a href="" class="waves-effect waves-light"><i class="fas fa-list" style="zoom:.6"></i> <span>工具</span> <i class="fas fa-chevron-down" aria-hidden="true" style="zoom:.6"></i></a><ul class="sub-nav menus_item_child"><li><a href="/friends"><i class="fas fa-address-book" style="margin-top:-20px;zoom:.6"></i> <span>留言</span></a></li></ul></li><li><a href="#searchModal" class="modal-trigger waves-effect waves-light"><i id="searchIcon" class="fas fa-search" title="搜索" style="zoom:.85"></i></a></li></ul><div id="mobile-nav" class="side-nav sidenav"><div class="mobile-head bg-color"><img src="/medias/book.gif" class="logo-img circle responsive-img"><div class="logo-name">丁一的博客</div><div class="logo-desc">DingYi的博客</div></div><ul class="menu-list mobile-menu-list"><li class="m-nav-item"><a href="/" class="waves-effect waves-light"><i class="fa-fw fas fa-home"></i> 首页</a></li><li class="m-nav-item"><a href="/tags" class="waves-effect waves-light"><i class="fa-fw fas fa-tags"></i> 标签</a></li><li class="m-nav-item"><a href="/categories" class="waves-effect waves-light"><i class="fa-fw fas fa-bookmark"></i> 分类</a></li><li class="m-nav-item"><a href="/archives" class="waves-effect waves-light"><i class="fa-fw fas fa-archive"></i> 时间轴</a></li><li class="m-nav-item"><a href="/about" class="waves-effect waves-light"><i class="fa-fw fas fa-user-circle"></i> 关于</a></li><li class="m-nav-item"><a href="javascript:;"><i class="fa-fw fas fa-list"></i> 工具 <span class="m-icon"><i class="fas fa-chevron-right"></i></span></a><ul><li><a href="/friends" style="margin-left:75px"><i class="fa fas fa-address-book" style="position:absolute;left:50px"></i> <span>留言</span></a></li></ul></li></ul></div></div></nav></header><div class="bg-cover pd-header post-cover" style="background-image:url(https://cdn.jsdelivr.net/gh/DreamUnity/cdn/image/titleBackground/7.jpg)"><div class="container" style="right:0;left:0"><div class="row"><div class="col s12 m12 l12"><div class="brand"><h1 class="description center-align post-title">JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】</h1></div></div></div></div></div><main class="post-container content"><link rel="stylesheet" href="/libs/tocbot/tocbot.css"><style>#articleContent h1::before,#articleContent h2::before,#articleContent h3::before,#articleContent h4::before,#articleContent h5::before,#articleContent h6::before{display:block;content:" ";height:100px;margin-top:-100px;visibility:hidden}#articleContent :focus{outline:0}.toc-fixed{position:fixed;top:64px}.toc-widget{width:345px;padding-left:20px}.toc-widget .toc-title{padding:35px 0 15px 17px;font-size:1.5rem;font-weight:700;line-height:1.5rem}.toc-widget ol{padding:0;list-style:none}#toc-content{padding-bottom:30px;overflow:auto}#toc-content ol{padding-left:10px}#toc-content ol li{padding-left:10px}#toc-content .toc-link:hover{color:#42b983;font-weight:700;text-decoration:underline}#toc-content .toc-link::before{background-color:transparent;max-height:25px;position:absolute;right:23.5vw;display:block}#toc-content .is-active-link{color:#42b983}#floating-toc-btn{position:fixed;right:15px;bottom:76px;padding-top:15px;margin-bottom:0;z-index:998}#floating-toc-btn .btn-floating{width:48px;height:48px}#floating-toc-btn .btn-floating i{line-height:48px;font-size:1.4rem}</style><div class="row"><div id="main-content" class="col s12 m12 l9"><div id="artDetail"><div class="card"><div class="card-content article-info"><div class="row tag-cate"><div class="col s7"><div class="article-tag"><a href="/tags/JVM/"><span class="chip bg-color">JVM</span> </a><a href="/tags/JAVA/"><span class="chip bg-color">JAVA</span> </a><a href="/tags/GC/"><span class="chip bg-color">GC</span></a></div></div><div class="col s5 right-align"><div class="post-cate"><i class="fas fa-bookmark fa-fw icon-category"></i> <a href="/categories/JVM/" class="post-category">JVM</a></div></div></div><div class="post-info"><div class="post-date info-break-policy"><i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp; 2023-02-09</div><div class="info-break-policy"><i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp; 9.5k</div><div class="info-break-policy"><i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp; 33 分</div><div id="busuanzi_container_page_pv" class="info-break-policy"><i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp; <span id="busuanzi_value_page_pv"></span></div></div></div><hr class="clearfix"><link rel="stylesheet" href="/libs/prism/prism.min.css"><div class="card-content article-card-content"><div id="articleContent"><p>内存动态分配和垃圾收集技术是JAVA和C++之间最大的区别之一</p><p>垃圾收集（Garbage Collection，GC）只办三件事：</p><ul class="lvl-0"><li class="lvl-2"><p>哪些内存需要回收</p></li><li class="lvl-2"><p>什么时候回收</p></li><li class="lvl-2"><p>如何回收</p></li></ul><h2 id="对于对象回收的方法"><a class="header-anchor" href="#对于对象回收的方法"></a>对于对象回收的方法</h2><ol><li class="lvl-3"><p>引用计数法：</p></li></ol><p>每处引用时+1，引用失效时-1，但是主流的Java虚拟机里面都没有选用引用计数算法来管理内存。</p><blockquote><p>比如很难解决对象之间相互循环引用的问题<code>objA.instance=objB ;objB.instance=objA</code><br><code>objA = null; objB = null</code><br>后,objA和objB未被回收</p></blockquote><ol start="2"><li class="lvl-3"><p>可达性分析算法</p></li></ol><p>主流的商用语言(Java\C#\Lisp)通过可达性分析(Reachability Analysis)进行判断。</p><p><mark>基本思路</mark>就是通过一系列称为“GC Roots”的根对象作为起始节点集，从这些节点开始，根据引用关系向下搜索，搜索过程所走过的路径称为“引用链”（Reference Chain），如果某个对象到GC Roots间没有任何引用链相连，或者用图论的话来说就是从GC Roots到这个对象不可达时，则证明此对象是不可能再被使用的。<br><img src="https://img-blog.csdnimg.cn/5651e4f7f4974ed89e1e00873ef4b9db.png#pic_center" alt="" width="400"></p><p>可以作为GC Roots的对象：</p><ul class="lvl-0"><li class="lvl-2"><p>虚拟机栈引用的对象，如各个线程被调用的方法栈中的<code>参数、局部变量、临时变量等</code></p></li><li class="lvl-2"><p>方法区中类静态属性引用的对象，如JAVA类的<code>引用类型静态变量</code></p></li><li class="lvl-2"><p>Native方法使用的对象</p></li><li class="lvl-2"><p>JAva虚拟机内部的引用，如<code>基本数据类型对应的Class对象</code>，一些<code>常驻的异常对象，系统类加载器</code></p></li><li class="lvl-2"><p>所有被同步锁（synchronized)持有的对象</p></li><li class="lvl-2"><p>反应Java虚拟机内部情况的JMXBean\JVMTI中注册的回调、本地代码缓存等</p></li></ul><h3 id="引用概念的修改"><a class="header-anchor" href="#引用概念的修改"></a>引用概念的修改</h3><p>判定对象是否存活都和“引用”离不开关系，但是过去的只有被引用和未被引用放在当今过于狭隘了。</p><blockquote><p>譬如我们希望能描述一类对象：当内存空间还足够时，能保留在内存之中，如果内存空间在进行垃圾收集后仍然非常紧张，那就可以抛弃这些对象——很多系统的缓存功能都符合这样的应用场景。</p></blockquote><p>JDK1.2后队引用进行了补充</p><ul class="lvl-0"><li class="lvl-2"><p>强引用 Strong：如<code>Obejct obj = new Object()</code>的引用赋值，<mark>GC永远不会回收</mark></p></li><li class="lvl-2"><p>软引用 Soft：述一些还有用，但非必须的对象。<mark>将溢出时</mark>，列入回收内存中进行二次回收</p></li><li class="lvl-2"><p>弱引用 Weak：那些非必须对象，但是它的强度比软引用更弱一些。<mark>生存到下一次GC发生的时候</mark></p></li><li class="lvl-2"><p>虚引用 Phantom：为“幽灵引用”或者“幻影引用”，它是最弱的一种引用关系。<mark>唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。</mark></p></li></ul><h3 id="对于对象的消亡判断"><a class="header-anchor" href="#对于对象的消亡判断"></a>对于对象的消亡判断</h3><p>在可达性判断中，若判断为<code>不可达</code>的对象时，那么是处于<code>缓刑</code>阶段。<mark>宣告一个对象死亡需要两次标记过程</mark></p><ol><li class="lvl-3"><p>在可达性分析后，发现没有与GC ROOT相连的引用链，则会被第一次标记</p></li><li class="lvl-3"><p>对象是否有必要执行finalize()方法。若没有覆盖该方法或该方法已经被虚拟机调用过，则视为不需要执行。若判断为需要执行，则将对象放置到名为<code>F-Queue</code>的队列中，然后由优先级低的Finalizer线程去执行。如果在finalize()中<code>重新与引用链上的任何一个对象建立关联则不会被回收</code>，否则被回收。</p></li></ol><h3 id="回收方法区"><a class="header-anchor" href="#回收方法区"></a>回收方法区</h3><p>主要回收两部分的内容：<code>废弃的常量</code>和<code>不再使用的类型</code></p><p>判断废弃的常量：假如一个字符串“java”曾经进入常量池中，但是当前系统又没有任何一个字符串对象的值是“java”<br>判断一个类型是否属于“不再被使用的类“：<br>·该类所有的实例都已经被回收，也就是Java堆中不存在该类及其任何派生子类的实例。<br>·加载该类的类加载器已经被回收，这个条件除非是经过精心设计的可替换类加载器的场景，如OSGi、JSP的重加载等，否则通常是很难达成的。<br>·该类对应的java.lang.Class对象没有在任何地方被引用，无法在任何地方通过反射访问该类的方法。</p><blockquote><p>Java虚拟机被允许对满足上述三个条件的无用类进行回收，这里说的仅仅是“被允许”，而并不是和对象一样，没有引用了就必然会回收。</p></blockquote><h2 id="垃圾收集算法"><a class="header-anchor" href="#垃圾收集算法"></a>垃圾收集算法</h2><p>大致可分为 引用计数式垃圾收集（Reference Counting GC）和 追踪式垃圾收集（Tracing GC）以下介绍的均为主流Java虚拟机中使用的<code>追踪式垃圾收集</code>的范畴</p><h3 id="分代收集理论"><a class="header-anchor" href="#分代收集理论"></a>分代收集理论</h3><ul class="lvl-0"><li class="lvl-2"><p>弱分代假说（Weak Generational Hypothesis）：绝大多数对象都是朝生夕灭的</p></li><li class="lvl-2"><p>强分代假说（Strong Generational Hypothesis）：熬过越多次垃圾收集过程的对象就越难以消亡。</p></li><li class="lvl-2"><p>跨代引用假说（Intergenerational Reference Hypothesis）：跨代引用相对于同代引用来说仅占极少数。</p></li></ul><p>由上诉的<code>前两条理论</code>产生的设计原则：：收集器应该将Java堆<code>划分出不同的区域</code>，然后将回收对象依据其年龄（年龄即对象熬过垃圾收集过程的次数）分配到不同的区域之中存储。</p><blockquote><p>若一个区域的大多数对象都是朝生夕灭的，则只关注如何保留少量存货。若剩下的都是难以消亡的，则集中放在一起，使用较低的频率来回收这个区域，兼顾了<code>时间开销</code>和<code>内存的空间</code>。</p></blockquote><p>如今，设计者将JAVA堆划分为<code>新生代</code>和<code>老年代</code>的两个区域。但是，<mark>对象不是孤立的，对象之间会存在跨代引用</mark>，由此，引出了<code>跨代引用假说</code>，即<mark>存在相引用关系的两个对象，是应该倾向于同时生存或者同时消亡的</mark>。由此，只需在新生代上建立一个<code>全局的数据结构（记忆集，Remebered Set）</code>，该结构把老年代划分为若干小块，标识出哪块存在跨代引用。因此发生Minor GC时，只将这些小块放入GC Root中进行扫描。</p><blockquote><p>不同分代的名词：<br>·部分收集（Partial GC）：指目标不是完整收集整个Java堆的垃圾收集，其中又分为：<br>■新生代收集（Minor GC/Young GC）：指目标只是<code>新生代的垃圾收集</code>。<br>■老年代收集（Major GC/Old GC）：指目标<code>只是老年代的垃圾收集</code>。目前只有CMS收集器会有单独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆，在不同资料上常有不同所指，需按上下文区分到底是指老年代的收集还是整堆收集。<br>■混合收集（Mixed GC）：指目标是收集<code>整个新生代以及部分老年代</code>的垃圾收集。目前只有G1收集器会有这种行为。<br>·整堆收集（Full GC）：收集整个Java堆和方法区的垃圾收集。</p></blockquote><h3 id="标记-清除算法（存活率低时较好）"><a class="header-anchor" href="#标记-清除算法（存活率低时较好）"></a>标记-清除算法（存活率低时较好）</h3><p>首先<code>标记出所有需要回收的对象</code>，在标记完成后，<code>统一回收</code>掉所有<code>被</code>标记的对象也可以反过来，<code>标记存活的对象</code>，<code>统一回收</code>所有<code>未被</code>标记的对象。</p><p>主要缺点有两个：</p><ol><li class="lvl-3"><p>执行效率不稳定。若被回收的过多，则需要进行大量的标记和清除工作，导致执行效率随数量变化</p></li><li class="lvl-3"><p>内存空间碎片化。在标记清楚后，出现不连续的内存空间，在分配大对象时候，需提前出发GC操作</p></li></ol><p><img src="https://img-blog.csdnimg.cn/64db144be015463283b029b85d2aa82a.png#pic_left" alt="" width="500"></p><h3 id="标记-复制算法（存活率低时较好）"><a class="header-anchor" href="#标记-复制算法（存活率低时较好）"></a>标记-复制算法（存活率低时较好）</h3><p>将可用内存按容量<code>划分</code>为大小相等的两块，每次只使用其中的一块。当这一块的内存用完了，就将<code>还存活着</code>的对象复制到另外一块上面，然后再把已使用过的内存空间<code>一次清理掉</code>。当下，多数采用此方法<code>回收新生代</code></p><p>主要缺点：将可用的内存缩小了一半，浪费空间<br><img src="https://img-blog.csdnimg.cn/a771f87c8f014c7e959773e43bf7bd9e.png#pic_left" alt="" width="500"></p><blockquote><p>改进后的Appel式回收:<br>把<code>新生代</code>分为一块<code>较大的Eden空间</code>和<code>两块较小的Survivor空间</code>，每次分配内存<code>只使用Eden和其中一块Survivor</code>。发生垃圾搜集时，将Eden和Survivor中<code>仍然存活的对象</code>一次性复制到另外一块Survivor空间上，然后直接<code>清理掉Eden和已用过的那块Survivor空间</code>。</p></blockquote><h3 id="标记-整理算法（存活率高时较好）"><a class="header-anchor" href="#标记-整理算法（存活率高时较好）"></a>标记-整理算法（存活率高时较好）</h3><p>标记过程与“标记-清除”一直，但后续让所有<code>存活</code>的对象都向<code>内存空间一端移动</code>，然后直接清理掉<code>边界以外</code>的内存，是移动式的，而清除是非移动式的<img src="https://img-blog.csdnimg.cn/441a322bbe624af9912a8457f9386c62.png#pic_left" alt="" width="500"><br>移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作，而清除会产生碎片化空间。因此。是否移动对象都存在弊端，<code>移动</code>则内存<code>回收时</code>会更复杂，<code>不移动</code>则内存<code>分配时</code>会更复杂，但是因<code>内存分配和访问相比垃圾收集频率要高</code>得多，这部分的耗时增加，总吞吐量仍然是下降的，因此移动相对划算。</p><p>也有一种“和稀泥的方法”：平时多数时间都采用标记-清除算法，的碎片化程度已经大到影响对象分配时，再采用标记-整理算法收集一次，以获得规整的内存空间</p><h2 id="HotSpot算法细节实现"><a class="header-anchor" href="#HotSpot算法细节实现"></a>HotSpot算法细节实现</h2><h3 id="根节点枚举"><a class="header-anchor" href="#根节点枚举"></a>根节点枚举</h3><p>在OopMap帮助下，可以快速准确的完成GC Roots枚举</p><hr><p>在根节点枚举时，<code>必须暂停用户线程</code>，但枚举必须在一个保障<code>一致性的快照</code>中进行，即不会在分析过程中，引用关系还在变化。即使在<code>号称停顿时间可控/几乎不停顿的CMS\G1\ZGC</code>中，根节点枚举也是必须要停顿的。但，目前JAVA虚拟机中采用的都是准确式垃圾收集``，故可以在<code>停顿下来后</code>，检查所有的上下文/全局的引用位置，可以<code>直接</code>得到对象引用（使用一组为<code>OopMap</code>的数据结构存放）。在<code>类加载完成后</code>，会把对象中的偏移量对应的类型数据计算出来，在<code>即时编译过程</code>中，在<code>特定位置</code>会记录下栈里的寄存器哪些位置是引用。<mark>故收集器在扫描时，可以直接查到引用信息，不需要从方法区/GC ROOT查找</mark></p><h3 id="安全点"><a class="header-anchor" href="#安全点"></a>安全点</h3><p>解决如何停顿用户线程，让虚拟机进入垃圾回收状态的问题，安全点机制保证了程序执行时，在不太长的时间内就会遇到可进入垃圾收集过程的安全点</p><hr><p>HotSpot并没有为每条指令生成OopMap，只是在“特定位置”记录这些信息，成为<code>安全点(Safepoint)</code>。</p><p>GC强制要求必须执行到安全点后才能执行</p><ul class="lvl-0"><li class="lvl-2"><p>安全点的选取，<mark>取决于“是否具有让程序长时间执行的特征”</mark>。如方法调用、循环跳转、异常跳转等指令序列复用</p></li><li class="lvl-2"><p>GC发生时，让所有线程跑到<code>最近的安全点</code>，然后停顿：<code>抢先式中断（Preemptive Suspension）</code>和<code>主动式中断（Voluntary Suspension）</code>。</p></li></ul><blockquote><p>抢先式（没人用了）：在GC发生时中断所有用户线程，若线程不在安全点上，则恢复执行，重新中断知道跑到安全点上主动式：GC需要中断线程时，不直接对线程操作，设置一个<code>标志位</code>，线程执行时主动地轮询该标志，为<code>真</code>时，到附近的安全点挂起。标志位置与安全点是重合的。</p></blockquote><h3 id="安全区域"><a class="header-anchor" href="#安全区域"></a>安全区域</h3><p>对于处于Sleep/Blocked状态的线程，解决无法响应虚拟机的中断请求。由此引入<code>安全区域（Safe Region）</code>来解决。</p><hr><p>安全区域是指能够确保在<code>某一段代码片段之中，引用关系不会发生变化</code>，因此，在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区域看作<code>被扩展拉伸了的安全点</code>。</p><p>执行到安全区域的代码时</p><ol><li class="lvl-3"><p>标识自己已经进入了安全区域，故GC发生时，不再管理这些线程</p></li><li class="lvl-3"><p>离开安全区域时，检查是否虚拟机完成了根节点枚举，若完成则继续执行，若没完成，则等待直到接收到信号</p></li></ol><h3 id="记忆集与卡表"><a class="header-anchor" href="#记忆集与卡表"></a>记忆集与卡表</h3><p>记忆集（Remember Set）：解决对象跨代引用的问题，记录从<code>非收集区域</code>指向<code>收集区域</code>的指针<code>集合</code>的抽象数据结构</p><blockquote><p>最简单的实现可以用<code>非收集区域</code>中所有<code>含跨代引用</code>的对象数组来实现</p></blockquote><hr><p>但是对此方案，维护与空间成本很高，但收集器只需要通过记忆集判断是否存在某指针，所以可以使用更粗的粒度进行记录。</p><blockquote><ul class="lvl-1"><li class="lvl-2"><p>字长精度：每个记录精确到一个<code>机器字长</code>（就是处理器的寻址位数，如常见的32位或64位，这个精度决定了机器访问物理内存地址的指针长度），该字包含跨代指针。</p></li><li class="lvl-2"><p>对象精度：每个记录精确到一个<code>对象</code>，该对象里有字段含有跨代指针。</p></li><li class="lvl-2"><p>卡精度：每个记录精确到一块<code>内存区域</code>，该区域内有对象含有跨代指针。</p></li></ul><p>第三种的卡精度也成为<code>“卡表”</code>，是最常用的记忆集实现形式。卡表定义了记忆集的记录精度、与堆内存的映射关系等。其中的每个元素对应着一块特定大小内存块，成为<code>卡页(Card Page)</code>，（大小通常为$2^N$的字节数）<img src="https://img-blog.csdnimg.cn/b4540d4c941e4021a1b1a21a32fa3b83.png#pic_left" alt="" width="300"><br>在卡页中不只一个对象，只要有<code>一个存在跨代指针</code>，则将对应的元素值标记为1，称为<code>元素变脏（Dirty）</code>，没有则表示为0。</p></blockquote><p>GC发生-&gt;筛选脏元素-&gt;得出存在跨代指针的内存块-&gt;放入GC Root中一并扫描</p><h3 id="写屏障"><a class="header-anchor" href="#写屏障"></a>写屏障</h3><p>为了解决卡表元素如何维护的问题，如：<mark>何时变脏、谁把他们变脏</mark></p><hr><ol><li class="lvl-3"><p>有其他分代区域中对象引用了本区域的对象时变脏</p></li><li class="lvl-3"><p>在<code>机器码层面</code>中，使用<code>写屏障</code>，把维护卡表的动作放到每个赋值操作之中</p></li></ol><blockquote><p><code>解释执行的字节码</code>，VM执行每条字节码指令，而在<code>编译执行</code>中，代码是纯粹的机器指令流了</p></blockquote><p><mark>写屏障</mark>可以看作在虚拟机层面对<code>“引用类型字段赋值”</code>这个动作的<code>AOP切面</code>，在引用对象赋值时会产生一个环形（Around）通知，供程序执行额外的动作，也就是说<code>赋值的前后都在写屏障的覆盖范畴内</code>。</p><blockquote><p>这边我认为AOP切面，就是与Spring中的权限验证功能类似，在执行时判断是否可以执行。将我们原本一条线执行的程序在中间切开加入了一些其他操作一样。</p></blockquote><p>在<code>赋值前</code>的部分为：<code>前屏障（Pre-Write Barrier）</code>，<code>后的为</code>：<code>后屏障（Post-Write Barrier）</code></p><p>引用写屏障后-&gt;为所有<code>赋值操作</code>生成指令-&gt;写屏障增加<code>更新卡表</code>操作</p><blockquote><p>存在伪共享（False Sharing）问题：因为<code>CPU</code>的缓存系统是以<code>缓存行（Cache Line）</code>为单位的，当多线程修改<code>互相独立的变量</code>，且变量共享<code>同一行</code>，会彼此影响（写回、无效化、同步）-&gt;降低了性能<mark>解决方法：1.先检查未被标记过才标记为脏 2.JDK7新增了参数，但是增加了一次判断的开销</mark></p></blockquote><h3 id="并发的可达性分析"><a class="header-anchor" href="#并发的可达性分析"></a>并发的可达性分析</h3><p>包含“标记”阶段是所有追踪式垃圾收集算法的共同特征，如果这个阶段会随着堆变大而等比例增加停顿时间，其影响就会波及几乎所有的垃圾收集器，因此并行收益是极大的</p><p>三色标记法：</p><ul class="lvl-0"><li class="lvl-2"><p>白色：尚未被垃圾收集器访问过</p></li><li class="lvl-2"><p>黑色：表示对象已经被垃圾收集器访问过，且这个对象的所有引用都已经扫描过</p></li><li class="lvl-2"><p>灰色：表示对象已经被垃圾收集器访问过，但这个对象上至少存在一个引用还没有被扫描过。<br><img src="https://img-blog.csdnimg.cn/8e456c68b37e4789a1267e6febfad5e3.png#pic_left" alt="" width="600"><br><img src="https://img-blog.csdnimg.cn/3949ec5129d4436c8d19b788c5b8d3ec.png#pic_left" alt="" width="600"></p></li></ul><blockquote><p>–&gt;由此总结出了产生对象消失问题的两条结论：</p><ol><li class="lvl-3">赋值器插入了一条或多条从黑色对象到白色对象的新引用；</li><li class="lvl-3">赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。</li></ol></blockquote><p>解决方案：</p><ol><li class="lvl-3"><p>增量更新（Incremental Update,破坏第一条）：<code>黑色</code>对象一旦<code>新插入</code>了指向白色对象的引用之后，它就<code>变回灰色</code>对象了。【新增白色引用时，<code>记录该引用</code>，扫描结束后，以<code>该引用的黑色</code>为根扫描】</p></li><li class="lvl-3"><p>原始快照（Snap At The Begining,SATB,破坏第二条）：无论引用关系删除与否，都会按照刚刚<code>开始扫描那一刻的对象图快照</code>来进行搜索。【删除白色引用时，<code>记录该引用</code>，扫描结束后，以<code>该引用的灰色</code>为根重新扫描】</p></li></ol><p>以上记录操作都是通过<code>写屏障</code>来实现的</p><h2 id="经典垃圾收集器"><a class="header-anchor" href="#经典垃圾收集器"></a>经典垃圾收集器</h2><p><img src="https://img-blog.csdnimg.cn/fe367689aafc4466ab181179eaef0183.png#pic_center" alt="" width="300"><br>链接的线指代两个收集器可以搭配使用。<br>== 不存在“万能”的收集器，只有对具体应用场景更合适的收集器==</p><h3 id="Serial收集器（标记-复制算法）"><a class="header-anchor" href="#Serial收集器（标记-复制算法）"></a>Serial收集器（标记-复制算法）</h3><p>该收集器是一个单线程工作的收集器，在进行垃圾收集时，必须暂停其他所有工作线程，直到它收集结束。<img src="https://img-blog.csdnimg.cn/ddbe46f6da5b4611bfc6e5f93ded2524.png#pic_left" alt="" width="700"><br>迄今为止，它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器，有着优于其他收集器的地方，那就是<mark>简单而高效</mark>（与其他收集器的单线程相比）<br>1.是所有收集器里额外内存消耗（Memory Footprint）最小的<br>2. 对于单核/处理器较少的环境，因为没有<code>线程交互</code>的开销，所以可以获得最高的<code>单线程效率</code></p><blockquote><p>在部分微服务中，内存一般不会特别大，所以垃圾收集的停顿时间也很短</p></blockquote><h3 id="ParNew收集器（标记-复制算法）"><a class="header-anchor" href="#ParNew收集器（标记-复制算法）"></a>ParNew收集器（标记-复制算法）</h3><p>是Serial收集器的并行版本，除了能并行其他与Serial收集器完全一致。</p><p>Serial/Serial Old收集过程：<img src="https://img-blog.csdnimg.cn/3e4ebcd8519b4d91bc3a3f0d8285c507.png#pic_center" alt="" width="700"><br>目前的用处：在JDK7之前遗留的系统中，只有他能与CMS（过去可以实现GC线程与用户线程同时工作的收集器）搭配使用，但是CMS作为<code>老年代</code>的无法与<code>新生代的Parallel Scavenge</code>搭配使用了，只有ParNew能搭配使用。后续也被G1收集器所代替。</p><blockquote><p>·并行（Parallel）：并行描述的是多条垃圾收集器线程之间的关系，说明同一时间有多条这样的线程在协同工作，通常默认<code>此时用户线程是处于等待状态</code>。<br>·并发（Concurrent）：并发描述的是垃圾收集器线程与用户线程之间的关系，说明<code>同一时间</code>垃圾收集器线程与用户线程<code>都在运行</code>，但不一定是并行的，可能会交替运行。由于用户线程并未被冻结，所以程序仍然能响应服务请求，但由于垃圾收集器线程占用了一部分系统资源，此时应用程序的处理的吞吐量将受到一定影响。</p></blockquote><h3 id="Parallel-Scavenge收集器（标记-复制算法）"><a class="header-anchor" href="#Parallel-Scavenge收集器（标记-复制算法）"></a>Parallel Scavenge收集器（标记-复制算法）</h3><p>达到一个可控制的<code>吞吐量（Throughput）</code><br>$$吞吐量 = \frac { 运行用户代码时间 } { 运行用户代码时间+运行垃圾收集时间 } $$</p><p>停顿时间越短，越适合交互频繁的程序，高吞吐量可以最高效率地利用处理器资源，适合<mark>在后台运算而不需要太多交互的分析任务</mark></p><p>用于控制吞吐量的参数：</p><ol><li class="lvl-3"><p>-XX：MaxGCPauseMillis：最大垃圾收集停顿时间</p></li><li class="lvl-3"><p>-XX：GCTimeRatio：直接设置吞吐量大小</p></li><li class="lvl-3"><p>-XX：UseAdaptiveSizePolicy：激活后不需要人工指定细节参数，通过运行情况动态调整，称为<code>自适应的调节策略（GC Ergonomics）</code></p></li></ol><p><mark>自适应调节策略也是Parallel Scavenge收集器区别于ParNew收集器的一个重要特性。</mark>以上皆为新生代收集器</p><hr><h3 id="Serial-Old收集器（标记-整理算法）"><a class="header-anchor" href="#Serial-Old收集器（标记-整理算法）"></a>Serial Old收集器（标记-整理算法）</h3><p>单线程收集器，主要提供给<code>客户端模式</code>下的HotSpot虚拟机使用，在<code>服务端模式下</code>，作为CMS的后备方案<br><img src="https://img-blog.csdnimg.cn/a18cb17c2d934006910eba03f158e498.png#pic_left" alt="" width="700"></p><h3 id="Parallel-Old收集器（标记-整理算法）"><a class="header-anchor" href="#Parallel-Old收集器（标记-整理算法）"></a>Parallel Old收集器（标记-整理算法）</h3><p>Parallel Scavenge的老年代版本，在<mark>注重吞吐量或者处理器资源较为稀缺的场合</mark>，都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合<br><img src="https://img-blog.csdnimg.cn/9977d1def8e04ae78af319f9a27d01b6.png#pic_left" alt="" width="700"></p><h3 id="CMS收集器（标记-清除算法）"><a class="header-anchor" href="#CMS收集器（标记-清除算法）"></a>CMS收集器（标记-清除算法）</h3><p>CMS(Concurrent Mark Sweep)一种以获取<code>最短回收停顿时间</code>为目标的收集器，集中在B/S系统的服务端上，<mark>较为关注服务的响应速度</mark>，带来良好的交互体验。</p><p>整个过程分为<strong>四个步骤</strong>：</p><ol><li class="lvl-3"><p>初始标记（CMS initial mark）</p></li><li class="lvl-3"><p>并发标记（CMS concurrent mark）</p></li><li class="lvl-3"><p>重新标记（CMS remark）</p></li><li class="lvl-3"><p>并发清除（CMS concurrent sweep）</p></li></ol><p><code>初始标记</code>只是标记GC Roots<code>直接关联</code>的对象，速度很快<code>并发标记</code>从<code>直接关联的对象遍历对象图</code>，<code>不需要停顿</code>用户线程，并发运行。<code>重新标记</code>为了<code>修正</code>并发标记期间，用户线程导致<code>标记有变动</code>的对象标记记录，<code>时间停顿</code>比初试标记<code>稍长</code>，<code>远比</code>并发标记短<code>清除阶段</code>为了<code>删除</code>在标记阶段判断的已死亡的对象，不需要移动存活对象，所以<code>与用户线程并发</code></p><p><mark>因此耗时最长的并发标记和并发清除都是可以与用户线程一起工作的</mark></p><p><img src="https://img-blog.csdnimg.cn/97cfbfb88a5c4f40a3032fe690d7ea6f.png" alt=""></p><p><mark>并发收集、停顿，但也有如下的缺点：</mark></p><ol><li class="lvl-3"><p>对<code>处理器</code>的资源非常<code>敏感</code>：【面向并发设计的程序都对处理器资源比较敏感】，并发时，占用了一部分线程、导致程序变慢、CMS中，在4核的情况下，GC线程只占用不超过25%，随着核心变多而下降。但是<code>不足4个</code>时，分出一半了。由此，产生了 "增量式并发收集器(Incremental Concurrent Mark Sweep/i-CMS)"的变种，模仿OS的抢占式多任务。在``并发标记、清除`时，让GC与用户讲题运行-&gt;时间变慢，下降幅度不明显</p></li><li class="lvl-3"><p>无法处理<code>“浮动垃圾”（Floating Garbage）</code>，因为<code>并发</code>，所以在GC时需要给用户线程留足够内存空间，所以不能等老年代快被填满时收集，JDK 5默认下是68%（偏保守了）</p></li></ol><blockquote><p>浮动垃圾：在CMS中，有可能出现“Con-current ModeFailure”失败进而导致另一次完全“Stop The World”的Full GC的产生。在运行自然就还会伴随有新的垃圾对象不断产生，但是出现在标记过程结束以后。CMS无法在当次收集中处理掉它们，只好留待下一次垃圾收集时再清理掉。</p></blockquote><ol start="3"><li class="lvl-3"><p>因为采用的<code>标记-清除</code>，因此有<code>大量空间碎片</code>产生。所以需要<code>内存碎片合并过程</code>。但是在移动存活对象，是无法并行的，所以在默认情况，每次进入Full GC先进行整理【-XX:CMSFullGCsBefore-Compaction负责，要求CMS收集器在执行过若干次（数量由参数值决定）不整理空间的Full GC之后，下一次进入Full GC前会先进行碎片整理】</p></li></ol><h3 id="Garbage-First收集器（整体标记-整理，局部标记-复制）"><a class="header-anchor" href="#Garbage-First收集器（整体标记-整理，局部标记-复制）"></a>Garbage First收集器（整体标记-整理，局部标记-复制）</h3><p>简称G1，里程碑式的成果，号称<mark>全功能的垃圾收集器(Fully-Featured Garbage Collector)</mark>，开创了<code>收集器</code>面向<code>局部收集</code>的设计思路，和基于<code>Region</code>的内存布局形式，它是主要面向<code>服务端应用</code>的垃圾收集器。</p><p><mark>核心思想</mark>：回收的衡量不再是属于哪个分代，而是取决于哪块的内存中的垃圾数量最多，<code>回收收益最大</code>，称为G1的<code>Mixed GC模式</code><mark>关键点</mark>：基于<code>Region</code>的堆内存布局【G1将连续的JAVA堆划分为多个大小相同的<code>独立区域（Region）</code>，每个Region可以根据需要<code>扮演</code>新生代的Eden、Survivor、老年代空间】。Region中的<code>HuMongous</code>区域专门存放大对象【大小超过Region的一半】</p><p>Region分区示意图：<img src="https://img-blog.csdnimg.cn/62b38fbe431e43f5b627bf810419c9cd.png#pic_left" alt="" width="400"></p><p><mark>在应用G1收集器所解决的问题</mark>：</p><ul class="lvl-0"><li class="lvl-2"><p>Region中存在<code>跨Region引用对象</code>时：使用<code>记忆集</code>可以避免从全栈中搜索GC Root，但是Region都维护包含自己的记忆集，即G1的记忆集是<code>双向卡表结构</code>，【哈希表，Key：Region的起始地址，Value：卡表的索引号】，因此占用更高的内存，相当于JAVA堆的10-20%</p></li><li class="lvl-2"><p>如何保证GC与用户<code>互不干扰</code>：1. 解决用户改变引用时，不打破原对象图结构：CMS使用了增量更新，而G1使用<code>原始快照</code>方式(SATB)。2. 收回过程中的新对象的分配：G1为每个Region设计两个<code>TAMS(Top at Mark Start)</code>指针，把Region一部分空间专门用于分配新对象。默认在这个地址的对象都是<code>被隐式标记过</code>，即默认存活，不纳入回收范围</p></li><li class="lvl-2"><p>如何建立可靠的<code>停顿预测模型</code>：以<code>衰减均值（Decaying Average）</code>为理论基础，在GC过程中，G1回记录Reion的<code>Region回收成本、记忆集脏卡数等可测量的成本</code>，得出<code>平均值、标准偏差、置信度</code>等信息。<code>衰减平均能够能准确的代表“最近的”平均状态</code>。即Region的统计状态<code>越新</code>，越能决定<code>回收价值</code>。</p></li></ul><p><mark>步骤</mark>：</p><ol><li class="lvl-3"><p>初始标记（Initial Marking）：标记GC Root直接关联的对象，并修改TAMS指针的值，与Minor GC同步完成</p></li><li class="lvl-3"><p>并发标记（Concurrent Marking）：从GC Root开始对堆进行可达性分析，耗时长可与用户并发进行，扫描后进行SATB处理</p></li><li class="lvl-3"><p>最终标记（Final Marking）：对用户进行短暂的暂停，处理并发后遗留的SATB记录</p></li><li class="lvl-3"><p>筛选回收（Live Data Counting And Evacuation）：更新Region的统计数据，对其回收价值/成本进行排序，根据用户的需求制定回收计划。在这使用标记-复制算法后清除整个旧Region，设计对象移动，暂停用户，多条GC线程并行完成。</p></li></ol><p><img src="https://img-blog.csdnimg.cn/ecf25c7dbd3b48d4adbfc13ab2aa14dc.png" alt=""><br><mark>达到延迟可控的情况下，尽可能提高吞吐量的目的。需求从一次把整个JAVA堆清干净，变为能够应付应用的内存分配速率（Allocation Rate）即可</mark>。与CMS的“标记-清除”算法不同，G1从<code>整体</code>来看是基于<code>“标记-整理”</code>，但从<code>局部</code>（两个Region之间）基于<code>“标记-复制”</code>，因此G1运作期间不会产生内存空间碎片。但G1在GC时的<code>内存占用（Footprint）</code>和程序运行的<code>负载（Overload）</code>都比CMS高</p><ol><li class="lvl-3"><p>内存占用角度：G1的卡表更复杂，每个Region都要有卡表，而CMS只有一份只处理老年代对新生代的引用</p></li><li class="lvl-3"><p>执行负载：CMS使用<code>写后屏障</code>，G1使用<code>写后</code>维护卡表的同时，因使用SATB，所以使用<code>写前屏障</code>跟踪并发时的并发情况。G1把写前和写后要做的事放入类似<code>消息队列</code>的结构中，进行<code>异步处理</code>。</p></li></ol><p>因此<mark>小内存选CMS，大内存选G1</mark>，平衡点在<code>6-8GB</code>之间</p><h2 id="低延迟垃圾收集器"><a class="header-anchor" href="#低延迟垃圾收集器"></a>低延迟垃圾收集器</h2><p>GC的衡量标准：内存占用（Footprint）、吞吐量（Throughput）、延迟（Latency），随着计算机硬件的发展，<mark>延迟的重要性日益凸显，越发备受关注</mark>。</p><p>各收集器的并发情况：<img src="https://img-blog.csdnimg.cn/0fdc5e4549b24c07945c193f33b4e16b.png#pic_left" alt="" width="400">浅色为必须挂起用户线程，深色表为GC与用户并发工作。在CMS/G1之前都要“Stop The World”停顿，在CMS/G1分别使用增量更新/原始快照，实现了<code>标记阶段的并发</code>。但是CMS中整理碎片空间也要“Stop The World”、G1可以按Region来回收，但是也是需要在筛选回收时暂停的。</p><p>目前Shenandoah/ZGC都是实验阶段的GC</p><h3 id="Shenandoah收集器"><a class="header-anchor" href="#Shenandoah收集器"></a>Shenandoah收集器</h3><p>在商用被ban了像是G1的下一代继承整，有着类似的堆内存布局。在管理内存的领域改进如下：</p><ol><li class="lvl-3"><p>支持<code>并发</code>的整理算法</p></li><li class="lvl-3"><p>默认<code>不适用分代</code> 收集</p></li><li class="lvl-3"><p><code>摒弃了</code>记忆集，改用<code>链接矩阵（Connection Matrix）</code>：N有对M的引用-&gt;标记matrix[N][M]。<img src="https://img-blog.csdnimg.cn/e33d2a4d7819442993d8ed3715acff94.png#pic_left" alt="" width="300"></p></li></ol><p><mark>步骤</mark>：</p><ol><li class="lvl-3"><p>初始标记（Initial Marking）：标记GC Root直接相关联的对象，短停</p></li><li class="lvl-3"><p><mark>并发标记</mark>（Concurrent Marking）：标记对象图中可达对象，与用户并发</p></li><li class="lvl-3"><p>最终标记（Final Marking）：处理剩余的SATB扫描，统计出回收价值最高的Region，组成回收集，小暂停</p></li></ol><p>---------以上与G1相同----------</p><ol start="4"><li class="lvl-3"><p>并发清理（Concurrent Cleanup）：清除<code>没有存活对象</code>的Region【称为 <code>Immediate Garbage Region</code>】</p></li><li class="lvl-3"><p><mark>并发回收</mark>（Concurrent Evacuation）：核心差异，先复制<code>回收集</code>中存活的对象到空Region中，使用<code>读屏障和“Brooks Pointers转发指针</code>解决，时间取决于回收集大小</p></li><li class="lvl-3"><p>初始引用更新（Initial Update Reference）：并未操作，只是为了确保GC完成了对象移动工作，有短暂停顿</p></li><li class="lvl-3"><p><mark>并发引用更新</mark>（Concurrent Update Reference）：开始<code>更新引用</code>，但是是按照<code>内存物理地址</code>的顺序搜索引用类型后修改</p></li><li class="lvl-3"><p>最终引用更新（Final Update Reference）：修改<code>堆</code>中的引用，要修改<code>GC Root</code>中的引用，最后一次停顿，与GC Root有关</p></li><li class="lvl-3"><p>并发清除（Concurrent Cleanup）：回收集中的所有Region都没有存活对象了，直接全回收掉</p></li></ol><p>黄色：被选入回收集的Region<br>绿色：还存活的对象蓝色：用户可以用来分配对象的Region<br><img src="https://img-blog.csdnimg.cn/97a6a24b958649589d118b09ce918675.png" alt=""></p><p><mark>支持并行整理的核心概念：Brooks Pointer</mark><br><img src="https://img-blog.csdnimg.cn/faf91d7a7f8544ce85b9616aed549a61.png#pic_left" alt="" width="150">在原有对象结构前添加一个在<code>不处于并发移动</code>时，引用<code>指向对象自己</code>的字段。【像句柄定位】</p><p><mark>存在的问题</mark>：</p><ol><li class="lvl-3"><p>执行效率的问题：保证并发时的<code>访问一致性</code>，需要设置读、写屏障拦截，与其他GC模型相比，加入了<code>额外的转发处理</code>，故而读代价很大，由此改进为<code>基于引用访问屏障（Load Reference Barrier）</code>，只拦截引用类型的读写操作</p></li><li class="lvl-3"><p>性能表现：<img src="https://img-blog.csdnimg.cn/51a9430d49e34afa9773b4bb4da8a8af.png#pic_left" alt="" width="500">未实现最大停顿在10毫秒内，<code>高运行负担</code>导致<code>吞吐量下降</code>，但是低延迟</p></li></ol><h3 id="ZGC收集器"><a class="header-anchor" href="#ZGC收集器"></a>ZGC收集器</h3><p><mark>基于Region内存布局，不设分代，使用读屏障、染色指针和内存多重映射等技术的标记-整理算法</mark></p><p>ZGC的Region具有动态性：动态创建、销毁、容量大小</p><ul class="lvl-0"><li class="lvl-2"><p>小型Region（Small Region）：2MB，存放小于256kb的对象</p></li><li class="lvl-2"><p>中型Region（Medium Region）：32MB，存放 256kb&lt; 对象 &lt; 4MB</p></li><li class="lvl-2"><p>大型Region（Large Region）：不固定，是2MB的整数倍，存放4MB以上的对象，不会被重分配，下文详细说</p></li></ul><p><img src="https://img-blog.csdnimg.cn/31cefbf68b0d4c26a9f73078d703cb74.png#pic_left" alt="" width="300"></p><p><mark>并发整理算法的实现</mark>：标志性的设计<code>染色指针技术（Colored Pointer）</code>，直接把标记信息记在<code>对象的指针</code>上，遍历<code>引用图</code>来标记引用。<br>64位的linux举例：<img src="https://img-blog.csdnimg.cn/0ad1219bd3654a3cb66d11fd72ddd198.png#pic_left" alt="" width="500"><br><mark>染色指针的三大优势</mark>：</p><ol><li class="lvl-3"><p>对象被<code>移走后</code>，region可以<code>立即</code>被释放和重用，不需要等待更新引用</p></li><li class="lvl-3"><p>可以大幅<code>减少</code>内存屏障的使用数量。只需要读屏障【写屏障通常是为了记录引用的变动情况】，一部分是因为<code>染色指针</code>，一部分是因为<code>没有分代收集</code>【没有跨代引用】</p></li><li class="lvl-3"><p>存储结构<code>可扩展</code>，来记录更多的对象标记、重定位过程相关的数据。若开发出linux前64中未使用的18位【这些不能用来寻址】</p></li></ol><p><mark>虚拟内存映射技术</mark>：<br>JVM重新定义指针中某几位的技术；在x86系统中，进程共用内存，不隔离。使用分页管理机制，实现线性地址到物理地址空间的映射。故而，linux/x86-64的ZGC使用了<code>多重映射（Multi-Mapping）</code>实现多个虚拟地址映射到同一个内存地址上【n-1】===》ZGC在虚拟地址识别的空间大于物理上的。</p><p><code>染色指针中的标志位</code>看作<code>分段符</code>，将这些不同的<code>地址段</code>映射到同一个内存空间，就可以正常寻址了。【原本是一个整体，现在切开了】<br><img src="https://img-blog.csdnimg.cn/63c8a333f4704b0896613db55c84e48f.png#pic_left" alt="" width="500"></p><p><mark>运行过程（四大阶段皆可并发）</mark></p><ol><li class="lvl-3"><p>并发标记（Concurrent Mark）：遍历对象图做可达性分析，但是在<code>指针上标记</code>，更新染色指针的<code>Marked 0、Marked 1</code></p></li><li class="lvl-3"><p>并发预备分配（Concurrent Prepare for Relocate）：根据查询得出<code>清理哪些Region</code>。每次GC扫描<code>所有</code>Region，扫描成本换记忆集维护成本。故<code>重分配集</code>只是<code>决定</code>存活的对象复制到别的Region中。</p></li><li class="lvl-3"><p><mark>并发重分配</mark>（Concurrent Relocate）：把<code>重分配集</code>存活的对象复制到新的Region中，并为每个Region维护一个<code>转发表（Forward Table）</code>，记录旧-&gt;新的引用。并且可以只从染色指针的<code>引用</code>上明确得知一个对象是否处于重分配集中，若用户线程<code>访问</code>当前对象，可以被预置的内存屏障所<code>拦截</code>，根据转发表<code>转发</code>到新的对象上，并<code>修正</code>引用值，称为<code>指针的“自愈”（Self-Healing）能力</code>。好处：1. 只有第一次会转发，比之前的每次的开销低。2. Region中的存活对象都复制后可以立即用于新对象的分配【转发表要留着】</p></li><li class="lvl-3"><p>并发重映射（Concurrent Remap）：修正整个堆中指向重分配集中就对象的所有引用。但<code>不是迫切任务</code>。因为引用是可以自愈的，故而合并到了下一次GC的并发标记中完成【因为都要遍历所有对象】</p></li></ol><p>ZGC是迄今为止最前沿的成果，几乎所有收集过程可并发，短暂停留只与GC Roots大小相关，在任何堆上都小于10ms</p><p><mark>但是</mark>：因为没有分代，所以能承受的<code>对象分配率不会太高</code>。【对一个大堆并发收集时，因为新对象的分配率高，所以有大量的新对象，ZGC只能全都当作存活对象，但是其中大多数是很快就死的===》产生了大量的浮动垃圾】，解决这个问题只能引入分代收集。</p><p><mark>性能方面</mark>：处于实验阶段</p><p>下图：左：吞吐量测试，右：ZGC停顿时间测试<br><img src="https://img-blog.csdnimg.cn/303b32472c0847b49693eb48280168ad.png#pic_left" alt="" width="300"><img src="https://img-blog.csdnimg.cn/51e331d792d34ebab9546514a82c00a1.png#pic_left" alt="" width="400"><br>PS：他也支持"NUMA-Aware"内存分配【专为多CPU/多核处理器】</p><h2 id="选择合适的垃圾收集器"><a class="header-anchor" href="#选择合适的垃圾收集器"></a>选择合适的垃圾收集器</h2><h3 id="Epsilon收集器"><a class="header-anchor" href="#Epsilon收集器"></a>Epsilon收集器</h3><p>一款<mark>不能够</mark>进行垃圾收集为卖点的垃圾收集器。但是还是有”自动内存管理子系统“的功能，这是GC收集器除了GC之外的工作。如果应用只要运行数分钟甚至数秒，只要Java虚拟机能正确分配内存，在堆耗尽之前就会退出，那显然运行负载极小、没有任何回收行为的Epsilon便是很恰当的选择。</p><h3 id="收集器的权衡"><a class="header-anchor" href="#收集器的权衡"></a>收集器的权衡</h3><p>应用程序：</p><ul class="lvl-0"><li class="lvl-2"><p>数据分析、科学计算===》吞吐量</p></li><li class="lvl-2"><p>SLA应用===》停顿时间、延迟</p></li><li class="lvl-2"><p>客户端应用、嵌入式应用===》GC的内存占用</p></li></ul><p>B/S系统==》延迟时间</p><p>钱多==》商用的Vega、Zing VM</p><p>钱不够要延迟低能用新版本==》ZGC</p><p>要稳定并在Window系统==》Shenandoah</p><p>遗留系统==》CMS，大内存G1</p><h3 id="虚拟机及垃圾收集器日记"><a class="header-anchor" href="#虚拟机及垃圾收集器日记"></a>虚拟机及垃圾收集器日记</h3><p>-Xlog参数：<br><code>-Xlog[:[selector][:[output][:[decorators][:output-options]]]]</code></p><p>其中最关键的selector是由tag【某个功能块的名字，如gc】与level【日记级别】共同组成。日志级别：Trace，Debug，Info，Warning，Error，Off，决定了详细程度<br>HotSpot的日志规则与Log4j、SLF4j框架一致。如果不置顶，默认值是uptime\level\tags：<br><code>[3.080s][info][gc,cpu] GC(5) User=0.03s Sys=0.00s Real=0.01s</code></p><h2 id="内存分配与回收策略"><a class="header-anchor" href="#内存分配与回收策略"></a>内存分配与回收策略</h2><ul class="lvl-0"><li class="lvl-3"><p>对象优先Eden分配，大多数情况下，对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时，虚拟机将发起一次Minor GC。</p></li><li class="lvl-3"><p>大对象直接进入老年代【大对象就是指需要大量连续内存空间的Java对象，最典型的大对象便是那种很长的字符串，或者元素数量很庞大的数组】。</p></li><li class="lvl-3"><p>如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半，年龄大于或等于该年龄的对象就可以直接进入老年代，无须等到-XX：MaxTenuringThreshold中要求的年龄。</p></li><li class="lvl-3"><p>在发生Minor GC之前，虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间，如果这个条件成立，那这一次Minor GC可以确保是安全的。如果不成立，则查看HandlePromotionFailure设置是否允许失败，若可以那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小，如果大于，将尝试进行一次Minor GC，尽管这次Minor GC是有风险的；如果小于，或者-XX：HandlePromotionFailure设置不允许冒险，那这时就要改为进行一次Full GC。</p></li></ul></div><hr><div class="reprint" id="reprint-statement"><div class="reprint__author"><span class="reprint-meta" style="font-weight:700"><i class="fas fa-user">文章作者: </i></span><span class="reprint-info"><a href="/about" rel="external nofollow noreferrer">丁一</a></span></div><div class="reprint__type"><span class="reprint-meta" style="font-weight:700"><i class="fas fa-link">文章链接: </i></span><span class="reprint-info"><a href="https://dingyidreams.com/2023/02/09/java-de-la-ji-shou-ji-qi-yu-nei-cun-fen-pei-ce-lue-yi-pian-wen-zhang-zhi-jie-kan-dong/">https://dingyidreams.com/2023/02/09/java-de-la-ji-shou-ji-qi-yu-nei-cun-fen-pei-ce-lue-yi-pian-wen-zhang-zhi-jie-kan-dong/</a></span></div><div class="reprint__notice"><span class="reprint-meta" style="font-weight:700"><i class="fas fa-copyright">版权声明: </i></span><span class="reprint-info">本博客所有文章除特別声明外，均采用 <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a> 许可协议。转载请注明来源 <a href="/about" target="_blank">丁一</a> !</span></div></div><script async defer>document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }</script><div class="tag_share" style="display:block"><div class="post-meta__tag-list" style="display:inline-block"><div class="article-tag"><a href="/tags/JVM/"><span class="chip bg-color">JVM</span> </a><a href="/tags/JAVA/"><span class="chip bg-color">JAVA</span> </a><a href="/tags/GC/"><span class="chip bg-color">GC</span></a></div></div><div class="post_share" style="zoom:80%;width:fit-content;display:inline-block;float:right;margin:-.15rem 0"><link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css"><div id="article-share"><div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div><script src="/libs/share/js/social-share.min.js"></script></div></div></div><style>#reward{margin:40px 0;text-align:center}#reward .reward-link{font-size:1.4rem;line-height:38px}#reward .btn-floating:hover{box-shadow:0 6px 12px rgba(0,0,0,.2),0 5px 15px rgba(0,0,0,.2)}#rewardModal{width:320px;height:350px}#rewardModal .reward-title{margin:15px auto;padding-bottom:5px}#rewardModal .modal-content{padding:10px}#rewardModal .close{position:absolute;right:15px;top:15px;color:rgba(0,0,0,.5);font-size:1.3rem;line-height:20px;cursor:pointer}#rewardModal .close:hover{color:#ef5350;transform:scale(1.3);-moz-transform:scale(1.3);-webkit-transform:scale(1.3);-o-transform:scale(1.3)}#rewardModal .reward-tabs{margin:0 auto;width:210px}.reward-tabs .tabs{height:38px;margin:10px auto;padding-left:0}.reward-content ul{padding-left:0!important}.reward-tabs .tabs .tab{height:38px;line-height:38px}.reward-tabs .tab a{color:#fff;background-color:#ccc}.reward-tabs .tab a:hover{background-color:#ccc;color:#fff}.reward-tabs .wechat-tab .active{color:#fff!important;background-color:#22ab38!important}.reward-tabs .alipay-tab .active{color:#fff!important;background-color:#019fe8!important}.reward-tabs .reward-img{width:210px;height:210px}</style><div id="reward"><a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a><div id="rewardModal" class="modal"><div class="modal-content"><a class="close modal-close"><i class="fas fa-times"></i></a><h4 class="reward-title">你的赏识是我前进的动力</h4><div class="reward-content"><div class="reward-tabs"><ul class="tabs row"><li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li><li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li></ul><div id="alipay"><img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码"></div><div id="wechat"><img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码"></div></div></div></div></div></div><script>$(function(){$(".tabs").tabs()})</script></div></div><style>.valine-card{margin:1.5rem auto}.valine-card .card-content{padding:20px 20px 5px 20px}#vcomments textarea{box-sizing:border-box;background:url(https://cdn.jsdelivr.net/gh/Yafine/cdn@3.1.1/social/comment_bg.png) 100% 100% no-repeat}#vcomments p{margin:2px 2px 10px;font-size:1.05rem;line-height:1.78rem}#vcomments blockquote p{text-indent:.2rem}#vcomments a{padding:0 2px;color:#4cbf30;font-weight:500;text-decoration:none}#vcomments img{max-width:100%;height:auto;cursor:pointer}#vcomments ol li{list-style-type:decimal}#vcomments ol,ul{display:block;padding-left:2em;word-spacing:.05rem}#vcomments ul li,ol li{display:list-item;line-height:1.8rem;font-size:1rem}#vcomments ul li{list-style-type:disc}#vcomments ul ul li{list-style-type:circle}#vcomments table,td,th{padding:12px 13px;border:1px solid #dfe2e5}#vcomments table,td,th{border:0}table tr:nth-child(2n),thead{background-color:#fafafa}#vcomments table th{background-color:#f2f2f2;min-width:80px}#vcomments table td{min-width:80px}#vcomments h1{font-size:1.85rem;font-weight:700;line-height:2.2rem}#vcomments h2{font-size:1.65rem;font-weight:700;line-height:1.9rem}#vcomments h3{font-size:1.45rem;font-weight:700;line-height:1.7rem}#vcomments h4{font-size:1.25rem;font-weight:700;line-height:1.5rem}#vcomments h5{font-size:1.1rem;font-weight:700;line-height:1.4rem}#vcomments h6{font-size:1rem;line-height:1.3rem}#vcomments p{font-size:1rem;line-height:1.5rem}#vcomments hr{margin:12px 0;border:0;border-top:1px solid #ccc}#vcomments blockquote{margin:15px 0;border-left:5px solid #42b983;padding:1rem .8rem .3rem .8rem;color:#666;background-color:rgba(66,185,131,.1)}#vcomments pre{font-family:monospace,monospace;padding:1.2em;margin:.5em 0;background:#272822;overflow:auto;border-radius:.3em;tab-size:4}#vcomments code{font-family:monospace,monospace;padding:1px 3px;font-size:.92rem;color:#e96900;background-color:#f8f8f8;border-radius:2px}#vcomments pre code{font-family:monospace,monospace;padding:0;color:#e8eaf6;background-color:#272822}#vcomments pre[class*=language-]{padding:1.2em;margin:.5em 0}#vcomments code[class*=language-],pre[class*=language-]{color:#e8eaf6}#vcomments [type=checkbox]:not(:checked),[type=checkbox]:checked{position:inherit;margin-left:-1.3rem;margin-right:.4rem;margin-top:-1px;vertical-align:middle;left:unset;visibility:visible}#vcomments b,strong{font-weight:700}#vcomments dfn{font-style:italic}#vcomments small{font-size:85%}#vcomments cite{font-style:normal}#vcomments mark{background-color:#fcf8e3;padding:.2em}#vcomments table,td,th{padding:12px 13px;border:1px solid #dfe2e5}table tr:nth-child(2n),thead{background-color:#fafafa}#vcomments table th{background-color:#f2f2f2;min-width:80px}#vcomments table td{min-width:80px}#vcomments [type=checkbox]:not(:checked),[type=checkbox]:checked{position:inherit;margin-left:-1.3rem;margin-right:.4rem;margin-top:-1px;vertical-align:middle;left:unset;visibility:visible}</style><div class="card valine-card" data-aos="fade-up"><div class="comment_headling" style="font-size:20px;font-weight:700;position:relative;padding-left:20px;top:15px;padding-bottom:5px"><i class="fas fa-comments fa-fw" aria-hidden="true"></i> <span>评论</span></div><div id="vcomments" class="card-content" style="display:grid"></div></div><script src="/libs/valine/av-min.js"></script><script src="/libs/valine/Valine.min.js"></script><script>new Valine({el:"#vcomments",appId:"ugrCXJtsapduyVcY1j6g0YkQ-gzGzoHsz",appKey:"XhFi3xbIoz6iDkFKQXzKTXi8",serverURLs:"",notify:!1,verify:!1,visitor:!0,avatar:"mm",pageSize:"10",lang:"zh-cn",placeholder:"just go go"})</script><article id="prenext-posts" class="prev-next articles"><div class="row article-row"><div class="article col s12 m6" data-aos="fade-up"><div class="article-badge left-badge text-color"><i class="fas fa-chevron-left"></i>&nbsp;上一篇</div><div class="card"><a href="/2023/03/06/shi-ti-guan-xi-chou-qu-qidn-query-based-instance-discrimination-network-for-relational-triple-extraction/"><div class="card-image"><img src="https://cdn.jsdelivr.net/gh/DreamUnity/cdn/image/titleBackground/3.jpg" class="responsive-img" alt="QIDN:Query-based Instance Discrimination Network for Relational Triple Extraction"> <span class="card-title">QIDN:Query-based Instance Discrimination Network for Relational Triple Extraction</span></div></a><div class="card-content article-content"><div class="summary block-with-text"></div><div class="publish-info"><span class="publish-date"><i class="far fa-clock fa-fw icon-date"></i>2023-03-06 </span><span class="publish-author"><i class="fas fa-bookmark fa-fw icon-category"></i> <a href="/categories/%E5%AE%9E%E4%BD%93%E5%85%B3%E7%B3%BB%E6%8A%BD%E5%8F%96/" class="post-category">实体关系抽取</a></span></div></div><div class="card-action article-tags"><a href="/tags/%E9%A1%B6%E4%BC%9A%E8%AE%BA%E6%96%87/"><span class="chip bg-color">顶会论文</span> </a><a href="/tags/%E4%B8%89%E5%85%83%E7%BB%84%E6%8A%BD%E5%8F%96/"><span class="chip bg-color">三元组抽取</span> </a><a href="/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"><span class="chip bg-color">自然语言处理</span></a></div></div></div><div class="article col s12 m6" data-aos="fade-up"><div class="article-badge right-badge text-color">下一篇&nbsp;<i class="fas fa-chevron-right"></i></div><div class="card"><a href="/2023/02/08/java-nei-cun-qu-yu-yi-pian-wen-zhang-zhi-jie-kan-dong/"><div class="card-image"><img src="https://cdn.jsdelivr.net/gh/DreamUnity/cdn/image/titleBackground/8.jpg" class="responsive-img" alt="JAVA内存区域【一篇文章直接看懂】"> <span class="card-title">JAVA内存区域【一篇文章直接看懂】</span></div></a><div class="card-content article-content"><div class="summary block-with-text"></div><div class="publish-info"><span class="publish-date"><i class="far fa-clock fa-fw icon-date"></i>2023-02-08 </span><span class="publish-author"><i class="fas fa-bookmark fa-fw icon-category"></i> <a href="/categories/JVM/" class="post-category">JVM</a></span></div></div><div class="card-action article-tags"><a href="/tags/JVM/"><span class="chip bg-color">JVM</span> </a><a href="/tags/JAVA/"><span class="chip bg-color">JAVA</span> </a><a href="/tags/%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F/"><span class="chip bg-color">内存区域</span></a></div></div></div></div></article></div><script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script><script type="text/javascript" src="/libs/prism/prism.min.js"></script><script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script><script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script><script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script></div><div id="toc-aside" class="expanded col l3 hide-on-med-and-down"><div class="toc-widget card" style="background-color:#fff"><div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div><div id="toc-content"></div></div></div></div><div id="floating-toc-btn" class="hide-on-med-and-down"><a class="btn-floating btn-large bg-color"><i class="fas fa-list-ul"></i></a></div><script src="/libs/tocbot/tocbot.min.js"></script><script>$(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('2'),
            headingSelector: 'h2, h3, h4, h5'
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });</script></main><script src="https://cdn.bootcss.com/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script>MathJax.Hub.Config({tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]]}})</script><footer class="page-footer bg-color"><link rel="stylesheet" href="/libs/aplayer/APlayer.min.css"><style>.aplayer .aplayer-lrc p{display:none;font-size:12px;font-weight:700;line-height:16px!important}.aplayer .aplayer-lrc p.aplayer-lrc-current{display:none;font-size:15px;color:#42b983}.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body{left:-66px!important}.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover{left:0!important}</style><div><div class="row"><meting-js class="col l8 offset-l2 m10 offset-m1 s12" server="netease" type="playlist" id="503838841" fixed="true" autoplay theme="#42b983" loop order="random" preload="auto" volume="0.7" list-folded="true"></meting-js></div></div><script src="/libs/aplayer/APlayer.min.js"></script><script src="/libs/aplayer/Meting.min.js"></script><div class="container row center-align" style="margin-bottom:0!important"><div class="col s12 m8 l8 copy-right">Copyright&nbsp;&copy; <span id="year">2022-2023</span> <a href="/about" target="_blank">丁一</a> |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a> |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a><br>&nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span class="white-color">166.5k</span> <span id="busuanzi_container_site_pv">&nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp; <span id="busuanzi_value_site_pv" class="white-color"></span> </span><span id="busuanzi_container_site_uv">&nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp; <span id="busuanzi_value_site_uv" class="white-color"></span></span><br><br></div><div class="col s12 m4 l4 social-link social-statis"><a href="https://github.com/DreamUnity" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50"><i class="fab fa-github"></i> </a><a href="mailto:704193119@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50"><i class="fas fa-envelope-open"></i> </a><a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=704193119" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 704193119" data-position="top" data-delay="50"><i class="fab fa-qq"></i> </a><a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50"><i class="fas fa-rss"></i></a></div></div></footer><div class="progress-bar"></div><div id="searchModal" class="modal"><div class="modal-content"><div class="search-header"><span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span> <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字" class="search-input"></div><div id="searchResult"></div></div></div><script type="text/javascript">$(function(){!function(t,s,i){"use strict";$.ajax({url:t,dataType:"xml",success:function(t){var e=$("entry",t).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get(),n=document.getElementById(s),r=document.getElementById(i);n.addEventListener("input",function(){var f='<ul class="search-result-list">',m=this.value.trim().toLowerCase().split(/[\s\-]+/);r.innerHTML="",this.value.trim().length<=0||(e.forEach(function(t){var n,e,r,s,i,l=!0,a=t.title.trim().toLowerCase(),c=t.content.trim().replace(/<[^>]+>/g,"").toLowerCase(),u=0===(u=t.url).indexOf("/")?t.url:"/"+u,o=-1,h=-1;""!==a&&""!==c&&m.forEach(function(t,e){n=a.indexOf(t),o=c.indexOf(t),n<0&&o<0?l=!1:(o<0&&(o=0),0===e&&(h=o))}),l&&(f+="<li><a href='"+u+"' class='search-result-title'>"+a+"</a>",e=t.content.trim().replace(/<[^>]+>/g,""),0<=h&&(s=h+80,(r=h-20)<0&&(r=0),0===r&&(s=100),s>e.length&&(s=e.length),i=e.substr(r,s),m.forEach(function(t){var e=new RegExp(t,"gi");i=i.replace(e,'<em class="search-keyword">'+t+"</em>")}),f+='<p class="search-result">'+i+"...</p>"),f+="</li>")}),f+="</ul>",r.innerHTML=f)})}})}("/search.xml","searchInput","searchResult")})</script><div id="backTop" class="top-scroll"><a class="btn-floating btn-large waves-effect waves-light" href="#!"><i class="fas fa-arrow-up"></i></a></div><script src="/libs/materialize/materialize.min.js"></script><script src="/libs/masonry/masonry.pkgd.min.js"></script><script src="/libs/aos/aos.js"></script><script src="/libs/scrollprogress/scrollProgress.min.js"></script><script src="/libs/lightGallery/js/lightgallery-all.min.js"></script><script src="/js/matery.js"></script><script src="https://ssl.captcha.qq.com/TCaptcha.js"></script><script src="/libs/others/TencentCaptcha.js"></script><button id="TencentCaptcha" data-appid="xxxxxxxxxx" data-cbfn="callback" type="button" hidden></button><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script async src="/libs/others/busuanzi.pure.mini.js"></script><script type="text/javascript" src="/libs/background/ribbon-dynamic.js" async></script><script src="/libs/instantpage/instantpage.js" type="module"></script></body></html>